////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2010-2011 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h>
#include "..\FM3.h"
#include "FM3_USART.h"

FM3_MFS_UART_TypeDef *g_Uart[8] = {
    (FM3_MFS_UART_TypeDef *)(FM3_MFS0_UART),
    (FM3_MFS_UART_TypeDef *)(FM3_MFS1_UART),
    (FM3_MFS_UART_TypeDef *)(FM3_MFS2_UART),
    (FM3_MFS_UART_TypeDef *)(FM3_MFS3_UART),
    (FM3_MFS_UART_TypeDef *)(FM3_MFS4_UART),
    (FM3_MFS_UART_TypeDef *)(FM3_MFS5_UART),
    (FM3_MFS_UART_TypeDef *)(FM3_MFS6_UART),
    (FM3_MFS_UART_TypeDef *)(FM3_MFS7_UART)
};

// COM INT
// 0   0
// 1   4
// 2   3
// 3   1
// 4   2
// 5   5
// 6   6
// 7   7
int IntIdxToCom[8] = {0, 3, 4, 2, 1, 5, 6, 7};
int ComToIntIdx[8] = {0, 4, 3, 1, 2, 5, 6, 7};

void FM3_USART_RX(int int_index)
{
    INTERRUPT_START;
    FM3_MFS_UART_TypeDef *uart = g_Uart[int_index];
    if ((uart->uart03.SSR & SSR_RDRF) != 0) {
        UINT16 c = (UINT16)(uart->uart03.RDR);
        USART_AddCharToRxBuffer(IntIdxToCom[int_index], (char)c);
        Events_Set(SYSTEM_EVENT_FLAG_COM_IN);
    } else {
        // lcd_printf("RXERR %02x     \r\n", uart->SSR);
        uart->uart03.SSR |= SSR_REC;
    }
    INTERRUPT_END;
}

void FM3_USART_TX(int int_index)
{
    INTERRUPT_START;
    char c;
    FM3_MFS_UART_TypeDef *uart = g_Uart[int_index];
    if ((uart->uart03.SSR & SSR_TDRE) != 0) {
        if (USART_RemoveCharFromTxBuffer(IntIdxToCom[int_index], c)) {
            uart->uart03.TDR = c;
        } else {
            uart->uart03.SCR &= ~(SCR_TIE);
        }
        Events_Set(SYSTEM_EVENT_FLAG_COM_OUT);
    }
    INTERRUPT_END;
}

void FM3_USART_Handle_IRQ(int int_index)
{
    FM3_MFS_UART_TypeDef *uart = g_Uart[int_index];
    if ((uart->uart03.SSR & (SSR_RDRF | SSR_ORE | SSR_FRE | SSR_PE)) != 0)    {
        FM3_USART_RX(int_index);
    } else {
        FM3_USART_TX(int_index);
    }
}

void MFS0RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(0);
}

void MFS1RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(1);
}

void MFS2RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(2);
}

void MFS3RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(3);
}

void MFS4RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(4);
}

void MFS5RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(5);
}

void MFS6RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(6);
}

void MFS7RX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(7);
}

void MFS0TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(0);
}

void MFS1TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(1);
}

void MFS2TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(2);
}

void MFS3TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(3);
}

void MFS4TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(4);
}

void MFS5TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(5);
}

void MFS6TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(6);
}

void MFS7TX_IRQHandler(void *param)
{
    FM3_USART_Handle_IRQ(7);
}

MFSTX_IRQHandler *txirqhandler[8] = {
    (MFSTX_IRQHandler *)MFS0TX_IRQHandler,
    (MFSTX_IRQHandler *)MFS1TX_IRQHandler,
    (MFSTX_IRQHandler *)MFS2TX_IRQHandler,
    (MFSTX_IRQHandler *)MFS3TX_IRQHandler,
    (MFSTX_IRQHandler *)MFS4TX_IRQHandler,
    (MFSTX_IRQHandler *)MFS5TX_IRQHandler,
    (MFSTX_IRQHandler *)MFS6TX_IRQHandler,
    (MFSTX_IRQHandler *)MFS7TX_IRQHandler
};

MFSRX_IRQHandler *rxirqhandler[8] = {
    (MFSRX_IRQHandler *)MFS0RX_IRQHandler,
    (MFSRX_IRQHandler *)MFS1RX_IRQHandler,
    (MFSRX_IRQHandler *)MFS2RX_IRQHandler,
    (MFSRX_IRQHandler *)MFS3RX_IRQHandler,
    (MFSRX_IRQHandler *)MFS4RX_IRQHandler,
    (MFSRX_IRQHandler *)MFS5RX_IRQHandler,
    (MFSRX_IRQHandler *)MFS6RX_IRQHandler,
    (MFSRX_IRQHandler *)MFS7RX_IRQHandler
};

//void CPU_INTC_Priority(UINT32 Irq_Index, UINT32 Irq_Pri);

//  FM3_GPIO->PFR0 = FM3_GPIO->PFR0 | 0x1C00;    // SCK4_0, SOT4_0, SIN4_0
//  FM3_GPIO->PFR5 = FM3_GPIO->PFR5 | 0x0038;    // SCK6_0, SOT6_0, SIN6_0, ...
//  FM3_GPIO->PFR6 = FM3_GPIO->PFR6 | 0x01C7;    // SCK5_0, SOT5_0, SIN5_0,
//  FM3_GPIO->PFR7 = FM3_GPIO->PFR7 | 0x001C;    //

BOOL CPU_USART_Initialize(int ComPortNum, int BaudRate, int Parity, int DataBits, int StopBits, int FlowValue)
{
    if (ComPortNum >= TOTAL_USART_PORT) return FALSE;
    if (Parity >= USART_PARITY_MARK) return FALSE;
    GLOBAL_LOCK(irq);
    int int_index = ComToIntIdx[ComPortNum];
    FM3_MFS_UART_TypeDef *uart = g_Uart[int_index];
    switch (int_index) {
    case 0:
#ifdef MB9BF506N
        FM3_GPIO->ADE &= ~0x8000;       // Disable AN15
        FM3_GPIO->PFR2 |= 0x0006;       // SIN0_0, SOT0_0, SCDK_0
        FM3_GPIO->EPFR07 |= 0x00000050; // SIN0_0, SOT0_0, SCK0_0
#endif
#ifdef MB9BF618T
        FM3_GPIO->ADE &= ~0x80000000;   // Disable AN31
        FM3_GPIO->PFR2 |= 0x0006;       // SIN0_0, SOT0_0, SCDK_0
        FM3_GPIO->EPFR07 |= 0x00000050; // SIN0_0, SOT0_0, SCK0_0
#endif
        break;
    case 1:
        break;
    case 2:
        break;
    case 3:
        FM3_GPIO->PFR4 |= 0x0300;       // SIN3_2, SOT3_2
        FM3_GPIO->EPFR07 |= 0x03C00000; // SIN3_2, SOT3_2
        break;
    case 4:
#ifdef MB9BF618T
        FM3_GPIO->PFR0 |= 0x0060;       // SIN4_2, SOT4_2, SCK4_2
        FM3_GPIO->EPFR08 |= 0x000000F0; // SIN4_2, SOT4_2, SCK4_2
#endif
        break;
    case 5:
        break;
    case 6:
        break;
    case 7:
        break;
    default:
        break;
    }
    // serial mode register
    // mode: UART
    // output enable
    // MSB first
    uart->uart03.SMR = (SMR_MD_UART | SMR_SOE);
    uart->uart03.SCR = 0x00;
    // stopbit
    // uart03->SMR |= SMR_BDS;
    if (StopBits == USART_STOP_BITS_TWO)
        uart->uart03.SMR |= (SMR_SBL);
    // baudrate
    uart->uart03.BGR = (UINT16)(((PCLK2 + ((UINT32)BaudRate / 2)) / (UINT32)BaudRate) - 1);
    // databit
    switch (DataBits) {
    case 6:
        uart->uart03.ESCR |= (ESCR_DATABITS_6);
        break;
    case 7:
        uart->uart03.ESCR |= (ESCR_DATABITS_7);
        break;
    case 9:
        uart->uart03.ESCR |= (ESCR_DATABITS_9);
        break;
    default:
        uart->uart03.ESCR |= (ESCR_DATABITS_8);
        break;
    }
    // paritybit
    switch (Parity) {
    case USART_PARITY_ODD:
        uart->uart03.ESCR |= (ESCR_PEN);
        break;
    case USART_PARITY_EVEN:
        uart->uart03.ESCR |= (ESCR_PEN | ESCR_P);
        break;
    default:
        // uart->ESCR |= 0;
        break;
    }
    CPU_INTC_ActivateInterrupt(MFS0RX_IRQn + int_index*2, rxirqhandler[int_index], 0);
    CPU_INTC_ActivateInterrupt(MFS0TX_IRQn + int_index*2, txirqhandler[int_index], 0);
    //CPU_INTC_Priority(MFS0RX_IRQn + int_index*2, 7);
    //CPU_INTC_Priority(MFS0TX_IRQn + int_index*2, 5);
    uart->uart03.SCR |= (SCR_RXE | SCR_TXE | SCR_RIE | SCR_TIE);
    return TRUE;
}

BOOL CPU_USART_Uninitialize(int ComPortNum)
{
    GLOBAL_LOCK(irq);
    int int_index = ComToIntIdx[ComPortNum];
    FM3_MFS_UART_TypeDef *uart = g_Uart[int_index];
    uart->uart03.SCR &= ~(SCR_RIE | SCR_TIE | SCR_TBIE | SCR_RXE | SCR_TXE);
    CPU_INTC_DeactivateInterrupt(MFS0RX_IRQn + int_index*2);
    CPU_INTC_DeactivateInterrupt(MFS0TX_IRQn + int_index*2);
    return TRUE;
}

BOOL CPU_USART_TxBufferEmpty(int ComPortNum)
{
    FM3_MFS_UART_TypeDef *uart = g_Uart[ComToIntIdx[ComPortNum]];
    return ((uart->uart03.SSR & SSR_TDRE) != 0);
}

BOOL CPU_USART_TxShiftRegisterEmpty(int ComPortNum)
{
    return CPU_USART_TxBufferEmpty(ComPortNum);
}

void CPU_USART_WriteCharToTxBuffer(int ComPortNum, UINT8 c)
{
    FM3_MFS_UART_TypeDef *uart = g_Uart[ComToIntIdx[ComPortNum]];
    while ((uart->uart03.SSR & SSR_TDRE) == 0);
#ifdef DEBUG
    ASSERT(CPU_USART_TxBufferEmpty(ComPortNum));
#endif
    uart->uart03.TDR = c;
}

void CPU_USART_TxBufferEmptyInterruptEnable(int ComPortNum, BOOL Enable)
{
    FM3_MFS_UART_TypeDef *uart = g_Uart[ComToIntIdx[ComPortNum]];
    if (Enable) {
        uart->uart03.SCR |= (SCR_TIE);
    } else
        uart->uart03.SCR  &= ~(SCR_TIE);
}

BOOL CPU_USART_TxBufferEmptyInterruptState(int ComPortNum)
{
    FM3_MFS_UART_TypeDef *uart = g_Uart[ComToIntIdx[ComPortNum]];
    if ((uart->uart03.SCR & SCR_TIE) != 0)
        return TRUE;
    else
        return FALSE;
}

void CPU_USART_RxBufferFullInterruptEnable(int ComPortNum, BOOL Enable)
{
    FM3_MFS_UART_TypeDef *uart = g_Uart[ComToIntIdx[ComPortNum]];
    if (Enable)
        uart->uart03.SCR |= (SCR_RIE);
    else
        uart->uart03.SCR &= ~(SCR_RIE);
}

BOOL CPU_USART_RxBufferFullInterruptState(int ComPortNum)
{
    FM3_MFS_UART_TypeDef *uart = g_Uart[ComToIntIdx[ComPortNum]];
    if ((uart->uart03.SCR & SCR_RIE) != 0)
        return TRUE;
    else
        return FALSE;
}

BOOL CPU_USART_TxHandshakeEnabledState(int ComPortNum)
{
    return TRUE;
}

void CPU_USART_ProtectPins(int ComPortNum, BOOL On)
{
    if (On) {
        CPU_USART_RxBufferFullInterruptEnable(ComPortNum, FALSE);
        CPU_USART_TxBufferEmptyInterruptEnable(ComPortNum, FALSE);
    } else {
        CPU_USART_TxBufferEmptyInterruptEnable(ComPortNum, TRUE);
        CPU_USART_RxBufferFullInterruptEnable(ComPortNum, TRUE);
    }
}

UINT32 CPU_USART_PortsCount()
{
    return TOTAL_USART_PORT;
}

void CPU_USART_GetPins(int ComPortNum, GPIO_PIN& rxPin, GPIO_PIN& txPin,GPIO_PIN& ctsPin, GPIO_PIN& rtsPin)
{
// ToDo
#ifndef UART47
        rxPin  = 0x21; // SIN0_0
        txPin  = 0x22; // SOT0_0
        ctsPin = 0xFF; //
        rtsPin = 0xFF; //
#else
        rxPin  = 0x05; // SIN4_2
        txPin  = 0x06; // SOT4_2
        ctsPin = 0x08; // CTS4_2
        rtsPin = 0x09; // RTS4_2
#endif
}

void CPU_USART_GetBaudrateBoundary(int ComPortNum, UINT32 &maxBaudrateHz, UINT32 &minBaudrateHz)
{
    // dummy
    UINT32 clk = PCLK2;
    maxBaudrateHz = clk >> 4;
    minBaudrateHz = clk >> 16;
}

BOOL CPU_USART_SupportNonStandardBaudRate( int ComPortNum )
{
    return TRUE;
}

BOOL CPU_USART_IsBaudrateSupported(int ComPortNum, UINT32 &BaudrateHz)
{
    if (BaudrateHz <= 230400)
        return TRUE;
    return FALSE;
}
